﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Drawing;
using System.ComponentModel;
using Asol.Reporting.Support;
using Asol.Reporting.Editor.Editor;
using Asol.Reporting.Editor.Components;
using Asol.Reporting.Editor.ComponentsLayer;

namespace Asol.Reporting.Editor.Data
{
	/// <summary>
	/// Svazek prvků v jednom místě sestavy.
	/// Záhlaví (stránky, skupiny, sloupce), detail, zápatí...
	/// </summary>
	public partial class ReportBand : ReportItem, IRevertItem
	{
		#region Konstrukce
        /// <summary>
        /// Svazek prvků v jednom místě sestavy. Konstruktor.
        /// </summary>
        public ReportBand()
        {
            this._Document = null;
            this.BandType = ReportBandType.None;
            this.Init();
        }
        /// <summary>
        /// Svazek prvků v jednom místě sestavy. Konstruktor.
        /// </summary>
        public ReportBand(ReportBandType bandType)
		{
			this._Document = null;
            this.BandType = bandType;
            this.Init();
        }
        /// <summary>
        /// Svazek prvků v jednom místě sestavy. Konstruktor.
        /// </summary>
        /// <param name="document"></param>
        public ReportBand(ReportDocument document, ReportBandType bandType)
		{
			this._Document = document;
            this.BandType = bandType;
            this.Init();
        }
        /// <summary>
        /// Inicializace
        /// </summary>
        protected void Init()
        {
            this.InitEditProperty();
            this.InitDataProperty();
        }
        /// <summary>
        /// Inicializuje vnitřní data třídy pro editor
        /// </summary>
        protected void InitEditProperty()
        {
            this._OwnerBandId = 0;
            this.LayerStandard = EditorLayer.Ground;
            this.LayerInteractive = EditorLayer.Ground;
            this.HeaderBackColor = Color.Azure;
            this._ColumnCount = 1;
            this.BandId = ++_BandId;
        }
        /// <summary>
        /// Resetuje čítač ID. Volat pouze po Clear celého dokumentu.
        /// </summary>
        public static void ResetId()
        {
            _BandId = 0;
        }
        /// <summary>
        /// ID bandu.
        /// Nové Band dostávají vždy nové ID.
        /// </summary>
        [Browsable(false)]
        public int BandId { get; private set; }
        private static int _BandId;
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "BandID=" + this.BandId.ToString() + "; " + this.FullHeaderText;
        }
        #endregion
        #region Public datové property - vazba bandu na OwnerBand, OwnerBandId, Document, Level
        /// <summary>
        /// Plné jméno tohoto bandu (= OwnerBand.FullHeaderText + "." + this.HeaderText)
        /// </summary>
        [Browsable(false)]
        public string FullHeaderText
        {
            get
            {
                string text = this.HeaderText;
                ReportBand ownerBand = this.OwnerBand;
                if (ownerBand != null)
                {
                    string ownerText = ownerBand.FullHeaderText;
                    if (!String.IsNullOrEmpty(ownerText))
                        text = ownerText + (String.IsNullOrEmpty(text) ? "" : ".") + text;
                }
                return text;
            }
        }
        /// <summary>
        /// Druh bandu
        /// </summary>
        public ReportBandType BandType { get; protected set; }
        /// <summary>
        /// Owner Band (svazek), do něhož patří tento svazek.
		/// Můj hostitel.
		/// Lze jej čas od času měnit (= přesunout this prvek někam jinam), možno i kopírovat do jiného parenta.
		/// </summary>
		[Browsable(false)]
		public ReportBand OwnerBand
        {
            get { return ((this._Document == null || this._OwnerBandId <= 0) ? null : this._Document.GetBand(this._OwnerBandId)); }
            internal set { this._OwnerBandId = (value == null ? 0 : value.BandId); }
        }
		/// <summary>
		/// ID Owner Bandu (svazek), do něhož patří tento svazek.
		/// Můj hostitel.
		/// Lze jej čas od času měnit (= přesunout this prvek někam jinam), možno i kopírovat do jiného parenta.
		/// </summary>
		[Browsable(false)]
		public int OwnerBandId
        {
            get { return this._OwnerBandId; }
            internal set { this._OwnerBandId = value; }
        }
		/// <summary>
		/// Dokument, do něhož patří tento svazek
		/// </summary>
		[Browsable(false)]
		public ReportDocument Document
		{
			get { return this._Document; }
            internal set { this._Document = value; }
		}
        /// <summary>Úložiště pro: Dokument, do něhož patří všechny Bands</summary>
        protected ReportDocument _Document;
        /// <summary>Úložiště pro: ID parent Bandu</summary>
        private int _OwnerBandId;
        /// <summary>
		/// Úroveň zanoření InnerBand, nejvyšší (RootBand) má 0
		/// </summary>
		[Browsable(false)]
		public int Level
		{
			get
			{
				if (this.IsRootBand) return 0;
				return this.OwnerBand.Level + 1;
			}
		}
		#region Jednoduché property typu Is* a Has*
		/// <summary>
        /// Příznak zda tento Band je Root = nejvyšší (true)  /  anebo má nějakého vlastníka (false).
		/// </summary>
		[Browsable(false)]
		public bool IsRootBand { get { return (this._OwnerBandId == 0); } }
		/// <summary>
		/// Příznak: je tento band Container? (tj. obsahuje nějaké InnerBands)
		/// </summary>
		[Browsable(false)]
		public bool IsContainer { get; private set; }
		#endregion
		#endregion
        #region Sestavení linearizovaného seznamu EditorBands a EditorComponents pro editor
        /// <summary>
		/// Metoda projde všechny své InnerBands a Components, které se mají zobrazovat v editoru.
        /// Patřičné prvky vybere a vloží do data.Result...
        /// Do prvků typu Bands dopočítá vizuální souřadnice v pixelech.
        /// Tato metoda je volána rekurzivně.
		/// </summary>
		/// <param name="args">Objekt, který zajišťuje přepočty logických a vizuálních souřadnic.</param>
		/// <param name="data">Objekt, který přenáší průběžná data mezi jednotlivými úrovněmi</param>
		/// <returns></returns>
		internal void FillEditorBandComponentList(EditEventArgs args, ReportBandLinearData data)
		{
            List<ReportBand> innerBands = data.GetInnerBands(this.BandId);
            this.IsContainer = (innerBands.Count > 0);
            this.EditorLogicalTop = data.LogicalY;

			data.ResultBandList.Add(this);

			PointF visualPoint = data.VisualPoint;
			SizeF visualSize = SizeF.Empty;
            if (this.IsCollapsed)
            {   // Zavřený Band: jeho výška je určena externě:
                visualSize = this.GetEditorBandCollapsedSize(args, data);
                data.LogicalY += data.LogicalCollapsedHeight;
            }
            else
            {   // Otevřený Band: záleží na jeho obsahu:
                if (this.IsContainer)
                {   // Container: obsahuje další Bands, výška je dána součtem výšek jeho Childs, šířka je dána jeho vlastní šířkou:
                    visualSize = this.GetEditorBandContainerSize(args, data, innerBands);
                }
                else
                {   // Datový Band: výška je dána jeho logickou výškou, šířka je od pozice X do konce prostoru vpravo:
                    visualSize = this.GetEditorBandComponentsSize(args, data);
                    data.LogicalY += this.LogicalHeight;
                    this.FillEditorComponents(args, data);
                    data.SetResultDocumentWidth(this.LogicalWidth);                                // Velikost dokumentu: registruji šířku (ta je uložena v logických jednotkách)
                }
            }
			this.AbsoluteVisualBound = new RectangleF(visualPoint, visualSize);
			data.VisualY = this.AbsoluteVisualBound.Bottom;
            data.SetResultDocumentHeight(args.ViewPortConvertor.LogicalFromVisual(data.VisualY));  // Velikost dokumentu: registruji výšku (tu převedu z vizuálních jednotek na jednotky logické)
		}
		/// <summary>
		/// Metoda určí velikost prvku this v situaci, kdy je Collapsed.
		/// Velikost je vizuální.
		/// </summary>
		/// <param name="args"></param>
		/// <param name="data"></param>
		/// <returns></returns>
        private SizeF GetEditorBandCollapsedSize(EditEventArgs args, ReportBandLinearData data)
		{
			float w = data.VisualTotalWidth - data.VisualX;
			float h = data.VisualCollapsedHeight;
			return new SizeF(w, h);
		}
		/// <summary>
		/// Metoda určí velikost prvku this v situaci, kdy prvek obsahuje vnitřní Bands (tedy když je IsContainer)
		/// </summary>
		/// <param name="args"></param>
		/// <param name="data"></param>
        /// <param name="innerBands"></param>
		/// <returns></returns>
        private SizeF GetEditorBandContainerSize(EditEventArgs args, ReportBandLinearData data, List<ReportBand> innerBands)
		{
			PointF visualPoint = data.VisualPoint;
            float currW = this.Document.GetBandHeaderVisualWidth(this.Level);       // Šířka Bandu this

            data.VisualX += currW;                                            // Pozice X (=Left) pro všechny vnořené Bands
			foreach (ReportBand innerBand in innerBands)
            	// Rekurzivně přidám data za všechny vnitřní Band (a tím pádem i rekurzivně za jejich InnerBands...)
				innerBand.FillEditorBandComponentList(args, data);
			
			data.VisualX = visualPoint.X;                                     // Obnovím pozici X pro this level
			return new SizeF(currW, data.VisualY - visualPoint.Y);
		}
		/// <summary>
		/// Metoda určí velikost prvku this v situaci, kdy prvek neobsahuje vnitřní Bands, ale komponenty.
        /// Velikost (výška) je dána this.LogicalHeight a měřítkem.
		/// </summary>
		/// <param name="args"></param>
		/// <param name="data"></param>
		/// <returns></returns>
        private SizeF GetEditorBandComponentsSize(EditEventArgs args, ReportBandLinearData data)
		{
			float w = data.VisualTotalWidth - data.VisualX;
            float h = args.ViewPortConvertor.VisualFromLogical(this.LogicalHeight);
			return new SizeF(w, h);
		}
        /// <summary>
        /// Metoda sestaví a vrátí seznam všech ReportBand (svazky sestavy), které se mají zobrazovat v editoru.
        /// Vrácený seznam obsahuje RootBand + všechny jeho vnitřní Band + rekurzivně, respektujíc stav ReportBand.Collapsed.
        /// Do každého z objektů vepíše jeho vizuální souřadnice v pixelech.
        /// </summary>
        /// <param name="args">Objekt, který zajišťuje přepočty logických a vizuálních souřadnic.</param>
        /// <param name="data">Objekt, který přenáší průběžná data mezi jednotlivými úrovněmi</param>
        /// <returns></returns>
        private void FillEditorComponents(EditEventArgs args, ReportBandLinearData data)
        {
            List<ReportComponent> components = data.GetInnerComponents(this.BandId); ;
            data.ResultComponentList.AddRange(components);
        }
        #endregion
        #region Vizuální vlastnosti
        /// <summary>
		/// Vizuální absolutní souřadnice, které mají počátek Y na logické souřadnici 0.00.
        /// Jsou určeny při sestavení linearizovaného seznamu EditorBands, v metodě this.FillEditorBandComponentList().
		/// Do CurrentVisualBound se upravuje o aktuální posun dokumentu ve směru Y.
		/// </summary>
        [Browsable(false)]
        public RectangleF AbsoluteVisualBound { get; protected set; }
        /// <summary>
        /// Pozice Top v editoru, v logických souřadnicích, kde souřadnice 0 je začátek reportu (nahoře).
        /// Je určena při sestavení linearizovaného seznamu EditorBands, v metodě this.FillEditorBandComponentList().
        /// Využívá ji každá komponenta pro určení své souřadnice při vykreslování.
        /// </summary>
        [Browsable(false)]
        public float EditorLogicalTop { get; protected set; }
        /// <summary>
        /// Logické rozměry svazku v milimetrech.
        /// U svazku, který je containerem, má význam pouze šířka.
        /// </summary>
        [Browsable(false)]
        public SizeF LogicalSize
        {
            get { return new SizeF(this.LogicalWidth, this.LogicalHeight); }
            // set { this.LogicalWidth = value.Width; this.LogicalHeight = value.Height; } 
        }
        /// <summary>
        /// Šířka svazku v logických jednotkách [milimetr].
        /// Využije se pouze pro svazky typu IsComponent.
        /// Vychází z nastavení stránky, dělení na sloupce a z velikosti okrajů.
        /// </summary>
        [Browsable(false)]
        public float LogicalWidth
        {
            get
            {
                if (this.IsRootBand)
                    return this.Document.ReportProperties.PaperDocumentArea.Width;
                return this.OwnerBand.LogicalWidth / (float)this.ColumnCount;
            }
        }
        /// <summary>
        /// Počet sloupců, na které se dělí tento Band proti svému OwnerBandu
        /// </summary>
        public int ColumnCount
        {
            get
            {
                if (this._ColumnCount < 0) return 1;
                return this._ColumnCount;
            }
            set
            {
                if (value < 1)
                    this._ColumnCount = 1;
                else if (value > 12)
                    this._ColumnCount = 12;
                else
                    this._ColumnCount = value;
            }
        }
        private int _ColumnCount;
        /// <summary>
		/// Výška svazku v logických jednotkách [milimetr].
		/// Využije se pouze pro svazky typu IsComponent.
		/// </summary>
		[Browsable(false)]
		public float LogicalHeight { get; set; }
        /// <summary>
        /// Resetuje svoje cachované VisualBounds
        /// </summary>
        public override void ResetVisualBounds()
        {
            base.ResetVisualBounds();
            this.CurrentRowVisualBound = null;
            this.CurrentRowVisibleBound = null;
        }
        /// <summary>
        /// Prostor řádku, v němž je aktuálně vykreslen tento Band, uvedeno v pixelech aktuálního controlu.
        /// Jde o teoretický prostor, vypočtený pro celou plochu řádku.
        /// V reálném controlu může být vykreslena jen jeho podmnožina, daná průnikem CurrentRowVisualBound a VisualArea pro komponenty.
        /// Má hodnotu null pouze před prvním vykreslením, a pak v těch Band, které jsou containery pro jiné Band a jejich řádky se nekreslí.
        /// </summary>
        public RectangleF? CurrentRowVisualBound { get; private set; }
        /// <summary>
        /// Prostor řádku, v němž je aktuálně vykreslen tento Band, uvedeno v pixelech aktuálního controlu.
        /// Jde o skutečný prostor, omezený do aktuální viditelné plochy řádku.
        /// Má hodnotu null pouze před prvním vykreslením, a pak v těch Band, které jsou containery pro jiné Band a jejich řádky se nekreslí.
        /// Pokud je řádek dostupný, ale není zrovna ve viditelné oblasti, je zde Empty rectangle - ale ne null.
        /// </summary>
        public RectangleF? CurrentRowVisibleBound { get; private set; }
        /// <summary>
        /// Editor: Barva pozadí záhlaví, odstín barvy řádku
        /// </summary>
        [Category("Layout")]
        [DisplayName("Barva pozadí")]
        [Description("Barva pozadí záhlaví a řádku v editoru")]
        public Color HeaderBackColor { get; set; }
        /// <summary>
        /// Text uvedený v záhlaví
        /// </summary>
        [Category("Layout")]
        [DisplayName("Titulek")]
        [Description("Text uvedený v záhlaví")]
        public string HeaderText { get; set; }
        /// <summary>
        /// Výchozí písmo pro nově přidávané prvky.
        /// Je null jen tehdy, když this nemá vztah na this.Document.
        /// </summary>
        [Category("Layout")]
        [DisplayName("Výchozí písmo")]
        [Description("Výchozí písmo pro nově přidávané prvky")]
        public Font DefaultFont
        {
            get
            {
                if (this._DefaultFont != null) return this._DefaultFont;
                if (this._Document != null) return this._Document.DefaultFont;
                return null;
            }
            set { this._DefaultFont = value; }
        }
        private Font _DefaultFont;
		/// <summary>
		/// Svazek je v editoru zavřen = minimalizován
		/// </summary>
		[Browsable(false)]
		internal bool IsCollapsed { get; set; }
		#endregion
		#region Kreslení a interaktivita
		#region Kreslení a interaktivita záhlaví (HeaderButton)
        /// <summary>
        /// <para>
        /// Vrátí vizuální prostor tohoto prvku, v pixelech.
        /// Typicky by metoda měla určit vizuální souřadnice na základě svých souřadnic logických.
        /// </para>
        /// <para>
        /// Pokud vrátí null, pak prvek není viditelný.
        /// Metoda je volána pouze jedenkrát v rámci Draw().
        /// Vrácená hodnota bude uložena do this.CurrentVisualBound.
        /// Pokud bude vráceno null, neprovede se kreslení ani další operace (deklarace segmentů).
        /// </para>
        /// </summary>
        protected override RectangleF GetVisualBound(EditEventArgs args)
		{
            RectangleF bandHeaderArea = args.ViewPortConvertor.GetVisualArea(EditorPart.BandHeader);   // Vizuální prostor pro Bands v rámci hostitelského Controlu
			PointF visualOriginPoint = args.ViewPortConvertor.VisualFromLogical(new PointF(0, 0));     // Vizuální souřadnice odpovídající logickému bodu 0/0
            RectangleF currentVisualBound = this.AbsoluteVisualBound;                                  // Vizuální absolutní souřadnice tohoto Band (kde bod 0/0 je počátkem Root bandu)
            currentVisualBound.X += bandHeaderArea.X;     // Posunutí v ose X je dáno počátkem prostoru BandHeader (v ose X)
            currentVisualBound.Y += visualOriginPoint.Y;  // Posunutí v ose Y je dáno posunem dokumentu v ose Y. Bod visualOriginPoint říká, na které vizuálním pixelu se nachází logický bod dokumentu 0/0.
            this.CurrentVisualArea = currentVisualBound;  // Hodnotu si uložíme (ačkoliv to za moment udělá i volající metoda EditorItem.Draw())
			return currentVisualBound;
		}
        /// <summary>
        /// Nastaví vizuální prostor tohoto prvku.
        /// Typicky by metoda měla na základě předaných vizuálních souřadnic určit souřadnice logické a někam do sebe si je uchovat.
        /// Pokud bude zanedlouho volaná metoda this.GetVisualBound() (proto, že změnou rozměru dojde k překreslení editoru), 
        /// měly by se vracet pokud možno identické souřadnice (jaké byly sem zadány).
        /// </summary>
        protected override void SetVisualBound(EditEventArgs args, RectangleF visualBound)
        {
            if (!this.IsCollapsed)
            {   // Jen NeCollapsed bands:
                if (this.IsContainer)
                {   // Pokud this je Container, pak nás zajímá editovaná šířka prvku, vepíšu ji do dokumentu:
                    this.Document.SetBandHeaderVisualWidth(this.Level, visualBound.Width);
                }
                else
                {   // Pokud this obsahuje komponenty, pak nás zajímá výška bandu po editaci. Z výšky vizuální si vypočtu výšku logickou, a tu uložím do sebe:
                    this.LogicalHeight = args.ViewPortConvertor.LogicalFromVisual(visualBound.Height);
                }
            }
        }
        /// <summary>
        /// Připraví aktivní segmenty = prostory, na které je myš citlivá
        /// </summary>
        /// <param name="args"></param>
        protected override void PrepareSegments(EditPaintEventArgs args)
        {
            if (!this.CurrentIsVisible) return;

            this.AllSegmentList = new List<EditorItemSegment>();

			RectangleF visualBound = this.CurrentVisualArea;
            float xl = visualBound.X;
            float xw = visualBound.Width;
            float xc = visualBound.X + xw / 2f;
            float xr = visualBound.X + xw - 1f;
            float yt = visualBound.Y;
            float yh = visualBound.Height;
            float ym = visualBound.Y + yh / 2f;
            float yb = visualBound.Y + yh - 1f;

            bool noLocked = !this.IsLocked;

            // Body, na nějž lze kliknout:
            bool bodyVisible = (this.HasInteractiveModeAny(EditItemInteractivityMode.LeftClick | EditItemInteractivityMode.RightClick));
            this.PrepareSegmentAddOne(this, EditorItemSegmentGripType.Body, bodyVisible,
                new SheetF(xl + 1f, yt + 1f, xw - 2f, yh - 2f),   // Interaktivní výška Body je menší než vizuální výška, ponechávám prostor pro interaktivní výšku dělítka BottomCenter.
                visualBound.Location,
                visualBound,
                SysCursorType.Default, SysCursorType.Default, SysCursorType.Default,
                EditorItemSegmentGripType.Body,
                false, false, false, false, false,
                EditMouseChangeType.EnterLeave | EditMouseChangeType.Click,
                args);
            
            // Hostitel dalších Bands má interaktivní dělítko na pravé svislé straně:
            bool middleRightVisible = (this.IsContainer && !this.IsCollapsed);
            this.PrepareSegmentAddOne(this, EditorItemSegmentGripType.MiddleRight, middleRightVisible,
                new SheetF(xr - 3f, yt, 6f, yh),
                new PointF(xr, ym),
                new RectangleF(xr - 1f, yt, 2f, yh),
                SysCursorType.VSplit, SysCursorType.VSplit, SysCursorType.VSplit,
                EditorItemSegmentGripType.MiddleRight,
                true, false, false, false, false,
                EditMouseChangeType.EnterLeave | EditMouseChangeType.Click | EditMouseChangeType.LeftDrag,
                args);

            // Datový Band má interaktivní dělítko dole:
            bool bottomCenterActive = (!this.IsContainer && !this.IsCollapsed);
            this.PrepareSegmentAddOne(this, EditorItemSegmentGripType.BottomCenter, bottomCenterActive,
                new SheetF(xl, yb - 3f, xw, 6f),                  // Interaktivní výška segmentu BottomCenter přesahuje i do nižšího prvku, ten nechává část svého prostoru k dispozici.
                new PointF(xl, yb),
                new RectangleF(xl, yb - 1f, xw, 2f),
                SysCursorType.HSplit, SysCursorType.HSplit, SysCursorType.HSplit,
                EditorItemSegmentGripType.BottomCenter,
                false, true, false, false, false,
                EditMouseChangeType.EnterLeave | EditMouseChangeType.Click | EditMouseChangeType.LeftDrag,
                args);
            
            // Segment pro zobrazení buttonu Collapse:
            bool buttonVisible = (yb > (yt + 11f));
            this.PrepareSegmentAddOne(this, 1001, buttonVisible,
                new SheetF(xl + 1f, yt + 3f, 14f, 10f),
                new PointF(xl + 2f, yt + 3f),
                new RectangleF(xl + 2f, yt + 4f, 8f, 6f),
                SysCursorType.Default, SysCursorType.Default, SysCursorType.Default,
                EditorItemSegmentGripType.UserDefined,
                false, false, false, false, false,
                EditMouseChangeType.EnterLeave | EditMouseChangeType.Click,
                args);
        }
        /// <summary>
        /// Vykreslí pozadí bandu + jeho viditelné okraje
        /// </summary>
        /// <param name="args"></param>
		protected override void DrawBackground(EditPaintEventArgs args)
		{
			RectangleF area = this.CurrentVisualArea;
			UiRenderer.DrawBackground(args.Graphics, area, this.HeaderBackColor, 35, RenderStyle.HorizontalHeader, this.CurrentMouseState);
            args.Graphics.DrawLine(Pens.Gray, area.Right - 0.5f, area.Y, area.Right - 0.5f, area.Bottom - 0.5f);
            args.Graphics.DrawLine(Pens.Gray, area.X, area.Bottom - 0.5f, area.Right - 0.5f, area.Bottom - 0.5f);
		}
        /// <summary>
        /// Metoda provede vykreslení obsahu (popředí) prvku.
        /// Metoda je volána vždy, bez podmínek.
        /// Vizuální prostor prvku (v pixelech) je uložen v this.CurrentVisualBound.Value, není null.
        /// Bázová metoda ve třídě ReportItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
        protected override void DrawContent(EditPaintEventArgs args)
        {
            if (!String.IsNullOrEmpty(this.HeaderText))
            {
				RectangleF bound = this.CurrentVisualArea;

                StringFormatFlags flags = StringFormatFlags.LineLimit | StringFormatFlags.NoWrap;
                if (this.IsContainer && !this.IsCollapsed)
                {
                    flags |= StringFormatFlags.DirectionVertical;
                    bound.Y += 10f;
                    bound.Height -= 10f;
                }
                else
                {
                    bound.Y += 3f;
                    bound.Height -= 3f;
                    bound.X += 10f;
                    bound.Width -= 10f;
                }
                StringFormat sf = new StringFormat(flags);

                var state = args.Graphics.Save();
                // args.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                // args.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                Color foreColor = this.HeaderBackColor.Contrast();
                using (Brush foreBrush = new SolidBrush(foreColor))
                {
                    args.Graphics.DrawString(this.HeaderText, SystemFonts.DefaultFont, foreBrush, bound, sf);
                }
                args.Graphics.Restore(state);
            }
        }
        /// <summary>
        /// Metoda je volána při kreslení prvku, docela na závěr všeho kreslení, a pouze pro segmenty typu (GripType == SegmentGripType.UserDefined).
        /// Potomek by si měl vykreslit UserDefined segmenty.
        /// Bázová metoda na třídě ReportItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        protected override void DrawUserDefinedSegmentOne(EditPaintEventArgs args, EditorItemSegment segment)
        {
            int segmentType = (int)segment.SegmentType;
            switch (segmentType)
            {
                case 1001:
                    this.DrawCollapseButton(args, segment);
                    break;
            }
        }
        /// <summary>
        /// Vykresluje button Collapse
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        protected void DrawCollapseButton(EditPaintEventArgs args, EditorItemSegment segment)
        {
            bool isCollapsed = this.IsCollapsed;
            bool mouseOnButton = (this.CurrentActiveSegment != null && this.CurrentActiveSegment.SegmentType == segment.SegmentType);
            if (!mouseOnButton)
                args.Graphics.DrawImage((isCollapsed ? Asol.Reporting.Support.Pics.IconLibraryMin.ArrowDownStd : Asol.Reporting.Support.Pics.IconLibraryMin.ArrowUpStd), segment.SegmentVisualBound.Location);
            else if (this.CurrentMouseState != EditMouseState.MouseLeftDown)
                args.Graphics.DrawImage((isCollapsed ? Asol.Reporting.Support.Pics.IconLibraryMin.ArrowDownHot : Asol.Reporting.Support.Pics.IconLibraryMin.ArrowUpHot), segment.SegmentVisualBound.Location);
            else
                args.Graphics.DrawImage((isCollapsed ? Asol.Reporting.Support.Pics.IconLibraryMin.ArrowDownHot : Asol.Reporting.Support.Pics.IconLibraryMin.ArrowUpHot), segment.SegmentVisualBound.Location);
                // args.Graphics.DrawImage((isCollapsed ? Asol.Reporting.Pics.IconLibraryMin.ArrowUpHot : Asol.Reporting.Pics.IconLibraryMin.ArrowDownHot), segment.SegmentVisualBound.Location);
        }
        /// <summary>
        /// Vrací štětec pro výplň segmentu
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        /// <returns></returns>
        protected override Brush GetBrushForSegment(EditPaintEventArgs args, EditorItemSegment segment)
        {
            Color color = Color.Black;
            switch (this.CurrentMouseState)
            {
                case EditMouseState.MouseLeftDown:
                    color = Color.GreenYellow;
                    break;
                case EditMouseState.OnControl:
                    color = Color.YellowGreen;
                    break;
                default:
                    color = Color.Black;
                    break;
            }
            return new SolidBrush(color);
        }
        /// <summary>
        /// Háček volaný při události Levé tlačítko myši se stisklo a uvolnilo, mezitím se nepohnulo. Klasický LeftClick.
        /// Metoda je volána pouze tehdy, pokud se kliklo na segment typu Body (this.CurrentActiveSegment.GripType == SegmentGripType.Body)
        /// </summary>
        /// <param name="args"></param>
        protected override void OnBodyLeftClick(EditInteractiveEventArgs args)
        {
            this.Document.SelectBand(this);
        }
        /// <summary>
        /// Háček volaný při události Levé tlačítko myši se stisklo a uvolnilo, mezitím se nepohnulo. Klasický LeftClick.
        /// Metoda je volána pouze tehdy, pokud se kliklo na některý segment (tzn. this.CurrentActiveSegment není null).
        /// Bázová metoda ve třídě ReportItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
        protected override void OnSegmentMouseLeftClick(EditInteractiveEventArgs args)
        {
            if (this.CurrentActiveSegment.GripType == EditorItemSegmentGripType.UserDefined && this.CurrentActiveSegment.SegmentType.Equals(1001))
            {
                this.IsCollapsed = !this.IsCollapsed;
                args.ReDrawLayer(this.LayerStandard);
            }
        }
		#endregion
		#region Kreslení podkladu řádku (neinteraktivní prostor)
		/// <summary>
		/// Vykreslí prostor řádku = podklad pod komponenty, Grid, Border řádku.
        /// Naplní do sebe (ReportBand) souřadnice viditelné plochy řádku CurrentRowVisualBound a CurrentRowVisibleBound.
		/// </summary>
		/// <param name="editorControl"></param>
		/// <param name="args"></param>
		internal void DrawRow(EditorControl editorControl, EditPaintEventArgs args)
		{
            RectangleF rowBound = this.GetEditorBandRowBound(args);
            this.CurrentRowVisualBound = rowBound;
            RectangleF visibleArea = args.VisualArea;
            visibleArea.Intersect(rowBound);
            this.CurrentRowVisibleBound = visibleArea;
            if (visibleArea.Width <= 0f || visibleArea.Height <= 0f) return;

            args.ActiveVisualAreaList.Add(new SheetF(visibleArea));
            args.IsVisible = true;
            args.IsInteractive = true;

            // args.
            args.Graphics.SetClip(visibleArea);

            // Podklad:
			using (Brush brush = this.GetEditorBandRowBrush())
			{
				args.Graphics.FillRectangle(brush, rowBound);
			}

            // Grid:
            this.DrawGrid(args, rowBound);

            // Borders:
            Color headerBackColor = this.HeaderBackColor;
            this.DrawRowBorder(args.Graphics, rowBound, rowBound.Y, headerBackColor.Morph(Color.GhostWhite, 0.6f));
            this.DrawRowBorder(args.Graphics, rowBound, rowBound.Bottom - 1f, headerBackColor.Morph(Color.DimGray, 0.6f));
		}
        /// <summary>
        /// Vykreslí jednu vodorovnou linku Border okolo řádku
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="rowBound"></param>
        /// <param name="y"></param>
        /// <param name="color"></param>
        protected virtual void DrawRowBorder(Graphics graphics, RectangleF rowBound, float y, Color color)
        {
            RectangleF bounds = new RectangleF(rowBound.X, y, rowBound.Width, 1f);
            using (SolidBrush brush = new SolidBrush(color))
            {
                graphics.FillRectangle(brush, bounds);
            }            
        }
		/// <summary>
		/// Vrátí vizuální souřadnice (v pixelech) pro vykreslení řádku za tento Band v prostoru editoru
		/// </summary>
		/// <param name="args"></param>
		/// <returns></returns>
        internal RectangleF GetEditorBandRowBound(EditEventArgs args)
		{
            RectangleF currentHeaderArea = this.CurrentVisualArea;             // Aktuální vizuální souřadnice záhlaví, jak byla naposledy určena metodou this.GetVisualBound()
            RectangleF currentComponentArea = args.ViewPortConvertor.VisualFromLogical(new RectangleF(0f, 0f, this.LogicalWidth, this.LogicalHeight));
            return new RectangleF(currentComponentArea.X, currentHeaderArea.Y, currentComponentArea.Width, currentHeaderArea.Height);
		}
		/// <summary>
		/// Vrátí výplň pro kreslení prostoru tohoto Band v editačním prostoru
		/// </summary>
		/// <returns></returns>
		internal virtual Brush GetEditorBandRowBrush()
		{
            return new SolidBrush(this.RowBackColor);
		}
        /// <summary>
        /// Obsahuje barvu pro kreslení prostoru tohoto Band v editačním prostoru.
        /// Tato barva je tvořena z 80% barvou RGB(250,252,255) = téměř bílá s modrým nádechem, a z 20% je tvořena barvou uvedenou v this.HeaderBackColor
        /// </summary>
        /// <returns></returns>
        internal virtual Color RowBackColor
        {
            get { return this.HeaderBackColor.Morph(Color.FromArgb(250, 252, 255), 0.80f); }
        }
        #endregion
		#region Kreslení Gridu
		/// <summary>
		/// Vykreslí mřížku řádku = na podkladu pod komponenty
		/// </summary>
		/// <param name="args"></param>
        /// <param name="rowBound">Prostor bandu, vizuální, bez omezení (celý band v pixelech)</param>
        protected void DrawGrid(EditPaintEventArgs args, RectangleF rowBound)
		{
            this.PrepareGridCoordinates(args, rowBound);
            switch (this.Document.ReportGridType)
			{
				case ShowGridType.ShowAsDots:
					this.DrawGridDots(args, rowBound);
					break;
				case ShowGridType.ShowAsCross:
                    this.DrawGridCross(args, rowBound);
					break;
				case ShowGridType.ShowAsLines:
                    this.DrawGridLines(args, rowBound);
					break;
			}
		}
		/// <summary>
		/// Vykreslí mřížku řádku = na podkladu pod komponenty, typ: Body
		/// </summary>
		/// <param name="args"></param>
        /// <param name="rowBound">Prostor bandu, vizuální, bez omezení (celý band v pixelech)</param>
        protected void DrawGridDots(EditPaintEventArgs args, RectangleF rowBound)
		{
            Color contrast = this.RowBackColor.Contrast(96);           // Vrací kontrastní barvu pro kreslení v prostoru řádku tohoto bandu
            using (Brush brush = new SolidBrush(contrast))
			{
                foreach (float y in this.GridYCoords)
                    foreach (float x in this.GridXCoords)
                        args.Graphics.FillRectangle(brush, x, y, 1f, 1f);
			}
		}
		/// <summary>
		/// Vykreslí mřížku řádku = na podkladu pod komponenty, typ: Cross
		/// </summary>
		/// <param name="args"></param>
        /// <param name="rowBound">Prostor bandu, vizuální, bez omezení (celý band v pixelech)</param>
        protected void DrawGridCross(EditPaintEventArgs args, RectangleF rowBound)
		{
            Image cross = Asol.Reporting.Support.Pics.IconLibraryMin.GridCrossRed_Cached;
            foreach (int y in this.GridYCoords)
                foreach (int x in this.GridXCoords)
                    args.Graphics.DrawImageUnscaled(cross, x - 2, y - 2);
		}
		/// <summary>
		/// Vykreslí mřížku řádku = na podkladu pod komponenty, typ: Čáry
		/// </summary>
		/// <param name="args"></param>
        /// <param name="rowBound">Prostor bandu, vizuální, bez omezení (celý band v pixelech)</param>
        protected void DrawGridLines(EditPaintEventArgs args, RectangleF rowBound)
		{
			float xl = rowBound.X + 1f;
			float xr = rowBound.Right - 2f;
			float yt = rowBound.Y + 2f;
			float yb = rowBound.Bottom - 3f;
            Color rowBackColor = this.RowBackColor;
            Color contrastX = rowBackColor.Contrast(32);
            Color contrastY = rowBackColor.Contrast(16);
            using (Pen penX = new Pen(contrastX))
            using (Pen penY = new Pen(contrastY))
			{
                foreach (float y in this.GridYCoords)
					args.Graphics.DrawLine(penX, xl, y, xr, y);

                foreach (float x in this.GridXCoords)
					args.Graphics.DrawLine(penY, x, yt, x, yb);
			}
		}
		/// <summary>
		/// Připraví (vypočítá) souřadnice pro zobrazování Gridu (pokud je toho zapotřebí).
		/// </summary>
		/// <param name="args"></param>
        /// <param name="rowBound">Prostor bandu, vizuální, bez omezení (celý band v pixelech)</param>
        protected void PrepareGridCoordinates(EditPaintEventArgs args, RectangleF rowBound)
		{
			RectangleF visualBound = this.GetEditorBandRowBound(args);
            RectangleF visibleArea = args.VisualArea;
            visibleArea.Intersect(visualBound);
            if (this.GridCoordinatesCorrect(args, visualBound, visibleArea)) return;        // Souřadnice jsou platné, není třeba je počítat stále znovu a znovu

            int[] gridXCoords, gridYCoords;
            this.Document.ReportGridCreateCoordinates(args, visualBound, visibleArea, out gridXCoords, out gridYCoords);
            this.GridXCoords = gridXCoords;
            this.GridYCoords = gridYCoords;
		}
		/// <summary>
		/// Zjistí, zda aktuálně vypočítané souřadnice Gridu jsou OK (vrací true) nebo je třeba vypočítat je znovu (vrací false).
		/// </summary>
		/// <param name="args">Kreslící data</param>
        /// <param name="visualBound">Prostor bandu, vizuální, bez omezení (celý band v pixelech). Na souřadnici (Location) je bod 0/0 tohoto bandu.</param>
        /// <param name="visibleArea">Viditelná část prostoru. Koordináty jsou shodné jako v visualBound, toto je viditelná podmnožina.</param>
		/// <returns></returns>
        protected bool GridCoordinatesCorrect(EditPaintEventArgs args, RectangleF visualBound, RectangleF visibleArea)
		{
            // Získám aktuální otisk dat, která určují souřadnice gridu:
            string mark = this.Document.ReportGridGetCoordMark(visualBound, visibleArea, args.ViewPortConvertor.VisualFromLogical(100f));

            // true, pokud mám souřadnice uložené, a pokud se data od posledně nezměnila:
            bool isCorrect = (
                this.GridXCoords != null && 
                this.GridYCoords != null && 
                this.LastGridMark == null && 
                String.Equals(this.LastGridMark, mark, StringComparison.InvariantCulture));

            // Uložím otisk, a vrátím true = beze změn:
            this.LastGridMark = mark;
            return isCorrect;
		}
		/// <summary>Paměť otisku GridMark, pro kterou byly posledně vypočteny souřadnice Gridu X a Y</summary>
		protected string LastGridMark;
		/// <summary>Uložené souřadnice Gridu ve směru X</summary>
		protected int[] GridXCoords;
		/// <summary>Uložené souřadnice Gridu ve směru Y</summary>
		protected int[] GridYCoords;
		#endregion
        #region Interaktivní změny
        /// <summary>
        /// Háček volaný při události Levé tlačítko myši začíná přesouvat segment, na němž se nachází (this.CurrentActiveSegment).
        /// </summary>
        /// <param name="args"></param>
        protected override void OnSegmentMouseLeftDragBegin(EditInteractiveEventArgs args)
        {
            this.Document.Reverter.StoreChangeBefore(this);
            base.OnSegmentMouseLeftDragBegin(args);
        }
        /// <summary>
        /// Přetahování segmentu myší, omezení rozměrů
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        protected override bool DragSegmentByMouse(EditInteractiveEventArgs args)
        {
            bool result = base.DragSegmentByMouse(args);

            if (result)
            {
                EditorItemSegment segment = this.CurrentActiveSegment;
                if (segment != null)
                {
                    RectangleF areaBefore = this.CurrentVisualArea;                               // V tuto chvíli obsahuje souřadnice PŘED provedením aktuálního pohybu.
                    if (segment.GripType == EditorItemSegmentGripType.MiddleRight)
                    {   // Změna šířky:
                        float widthBefore = areaBefore.Width;
                        float widthRequired = segment.MovedSegmentPoint.X - areaBefore.X;         // Požadovaná šířka (vizuální, daná pohybem myši)
                        float widthChecked = this.Document.CheckBandHeaderWidth(widthRequired, widthBefore);   // Přípustná souřadnice Width, prověřená dokumentem (ve vizuálních jednotkách)
                        // Pokud segment nemůžeme posunout tam, kam by (uživatel) chtěl, posuneme ho tam, kam je možno:
                        if (widthChecked != widthRequired)
                            segment.MovedSegmentPoint = new PointF(areaBefore.X + widthChecked, segment.MovedSegmentPoint.Y);
                        // Pokud přípustná souřadnice je shodná jako výchozí, pak de facto nedochází ke změně:
                        if (widthChecked == widthBefore)
                            result = false;
                    }
                    if (segment.GripType == EditorItemSegmentGripType.BottomCenter)
                    {   // Změna výšky - tady přejdu z vizuální na logickou výšku, v logické proběhne kontrola, a pak přejdu zpátky na vizuální:
                        float heigthVisualBefore = areaBefore.Height;
                        float heigthLogicalBefore = args.ViewPortConvertor.LogicalFromVisual(heigthVisualBefore);
                        float heightVisualRequired = segment.MovedSegmentPoint.Y - areaBefore.Y;
                        float heightLogicalRequired = args.ViewPortConvertor.LogicalFromVisual(heightVisualRequired);
                        float heightLogicalChecked = this.CheckBandHeight(heightLogicalRequired, heigthLogicalBefore);
                        float heightVisualChecked = args.ViewPortConvertor.VisualFromLogical(heightLogicalChecked);

                        // Pokud segment nemůžeme posunout tam, kam by (uživatel) chtěl, posuneme ho tam, kam je možno:
                        if (heightVisualChecked != heightVisualRequired)
                            segment.MovedSegmentPoint = new PointF(segment.MovedSegmentPoint.X, areaBefore.Y + heightVisualChecked);
                        // Pokud přípustná souřadnice je shodná jako výchozí, pak de facto nedochází ke změně:
                        if (heightVisualChecked == heigthVisualBefore)
                            result = false;
                    }
                }
            }

            return result;
        }
        /// <summary>
        /// Háček volaný při události Levé tlačítko myši přesouvá segment, na němž se nachází (this.CurrentActiveSegment).
        /// Bázová metoda ve třídě EditorItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
        protected override void OnSegmentMouseLeftDragMove(EditInteractiveEventArgs args)
        {
            // Tady není vhodné místo pro omezení pohybu; k tomu slouží metoda this.DragSegmentByMouse()
            base.OnSegmentMouseLeftDragMove(args);
            this.SetVisualBound(args, this.CurrentVisualArea);
        }
        /// <summary>
        /// Háček volaný při události Levé tlačítko myši končí přesun segmentu, na němž se nachází (this.CurrentActiveSegment).
        /// Bázová metoda ve třídě EditorItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
        protected override void OnSegmentMouseLeftDragEnd(EditInteractiveEventArgs args)
        {
            base.OnSegmentMouseLeftDragEnd(args);
            this.Document.Reverter.StoreChangeAfter(this);
            this.Document.UIRefresh();
        }
        /// <summary>
        /// Metoda prověří, zda band může mít požadovanou výšku. Pokud ano, vrátí ji.
        /// Pokud ne, vrátí jinou vhodnou výšku se zohledněním výšky heigthLogicalBefore (kterou měl band před pokusem o pohyb).
        /// Metoda pracuje s logickými jednotkami, nestará se o přepočty na vizuální jednotky a zpátky.
        /// </summary>
        /// <param name="heightLogicalRequired"></param>
        /// <param name="heigthLogicalBefore"></param>
        /// <returns></returns>
        protected float CheckBandHeight(float heightLogicalRequired, float heigthLogicalBefore)
        {
            if (heightLogicalRequired > heigthLogicalBefore) return heightLogicalRequired;

            // Řekněme, že požadovaná výška je v pořádku:
            float heightLogicalChecked = heightLogicalRequired;
            List<ReportComponent> components = this.Document.GetEditorComponentsForBand(this.BandId);
            if (components != null)
            {
                foreach (ReportComponent component in components)
                {
                    // Pro každou komponentu určím nejmenší výšku, do které se ještě vejde:
                    float componentMinHeight = this.GetMinHeightForComponent(component);
                    if (componentMinHeight > heightLogicalChecked)
                        heightLogicalChecked = componentMinHeight;
                }

                // Pokud zkontrolovaná povolená výška (Checked) je větší než požadovaná, musím to nějak řešit:
                if (heightLogicalChecked > heightLogicalRequired)
                {
                    if (heightLogicalRequired < heigthLogicalBefore)
                    {   // Pokud uživatel výšku zmenšuje, tak mu to nedovolíme, dovolíme mu jen to menší z (Checked, Before):
                        // Proč rovnou nevrátím Checked? Protože by to bylo hloupý, výšku zmenšovat (nahoru) a ona uskočí někam dolů.
                        heightLogicalChecked = (heightLogicalChecked < heigthLogicalBefore ? heightLogicalChecked : heigthLogicalBefore);
                    }
                    else if (heightLogicalRequired > heigthLogicalBefore)
                    {   // Uživatel výšku zvětšuje, což je jen dobře, to mu dovolíme: (to je tak obecná pravda, že je aplikována již na začátku této metody).
                        heightLogicalChecked = heightLogicalRequired;
                    }
                }
            }
            if (heightLogicalChecked < 0f) heightLogicalChecked = 0f;
            return heightLogicalChecked;
        }
        /// <summary>
        /// Vrátí nejmenší výšku this bandu, aby se do něj vešla daná komponenta a nepřesahovala dolů pod Band
        /// </summary>
        /// <param name="component"></param>
        /// <returns></returns>
        private float GetMinHeightForComponent(ReportComponent component)
        {
            float minHeight = 0f;
            RelativeRectangleF relRec = component.RelativeBounds;
            bool topInThis = (component.OwnerTopBandId == this.BandId);            // Vršek komponenty patří do zdejšího bandu
            bool bottomInThis = (component.OwnerBottomBandId == 0 || component.OwnerBottomBandId == this.BandId);  // Spodek komponenty patří do zdejšího bandu
            bool allInThis = topInThis && bottomInThis;                            // Celá komponenta leží v tomto bandu
            AnchorType anchorY = component.AnchorY;
            switch (anchorY)
            {
                case AnchorType.After:
                    // Komponenta je přilepena k dolnímu okraji dolního bandu, drží si svoji výšku:
                    if (allInThis)
                    {   // Komponenta je celá v jednom bandu (= tomto) a je přilepená dolů:
                        // Limit výšky tohoto bandu je dán dolním okrajem pod komponentou a její výškou:
                        minHeight = relRec.YSize + relRec.YBottomAfter;
                    }
                    else if (bottomInThis)
                    {   // Komponenta je ve dvou různých bandech, v tomto má svůj dolní okraj, je přilepená dolů a nemění svoji výšku, posouvá svůj Top okraj:
                        // Limit výšky tohoto bandu je dán pouze dolním okrajem pod komponentou, aby dolní okraj nevyjel nad tento band:
                        minHeight = relRec.YBottomAfter;
                    }
                    else if (topInThis)
                    {   // Komponenta je ve dvou různých bandech, v tomto má svůj horní okraj, a je přilepená dolů:
                        // Limit výšky tohoto bandu je dán horním okrajem (Top), dimenzí Begin, tak aby horní okraj nesklouzl pod tento band:
                        minHeight = relRec.YTopBegin;
                    }
                    break;

                case AnchorType.Both:
                    // Komponenta je přilepena k hornímu okraji horního bandu, i k dolnímu okraji dolního bandu, a mění svoji výšku:
                    if (allInThis)
                    {   // Komponenta je celá v jednom bandu (= tomto) a je přilepená nahoru i dolů:
                        // Limit výšky tohoto bandu je dán součtem horního a dolního okraje komponenty, tak aby její výška neklesla pod nulu:
                        minHeight = relRec.YTopBegin + relRec.YBottomAfter;
                    }
                    else if (bottomInThis)
                    {   // Komponenta je ve dvou různých bandech, v tomto má svůj dolní okraj, a je přilepená nahoru i dolů:
                        // Limit výšky tohoto bandu je dán hodnotou YBottomAfter, aby dolní okraj nevyjel nad tento band:
                        minHeight = relRec.YBottomAfter;
                    }
                    else if (topInThis)
                    {   // Komponenta je ve dvou různých bandech, v tomto má svůj horní okraj, a je přilepená nahoru i dolů:
                        // Limit výšky tohoto bandu je dán horním okrajem (Top), dimenzí Begin, tak aby horní okraj nesklouzl pod tento band:
                        minHeight = relRec.YTopBegin;
                    }
                    break;

                default:
                    // Komponenta je přilepena k hornímu okraji horního bandu, drží si svoji výšku:
                    if (allInThis)
                    {   // Komponenta je celá v jednom bandu (= tomto) a je přilepená jen nahoru (docela běžná situace):
                        // Limit výšky tohoto bandu je dán součtem horního okraje komponenty a její výšky, tak aby band nepřekryl její dolní okraj:
                        minHeight = relRec.YTopBegin + relRec.YSize;
                    }
                    else if (bottomInThis)
                    {   // Komponenta je ve dvou různých bandech, v tomto má svůj dolní okraj, a je přilepená jen nahoru (docela běžná situace):
                        // Limit výšky tohoto bandu je dán hodnotou YBottomBegin, tak aby tento band nepřekryl její dolní okraj:
                        minHeight = relRec.YBottomBegin;
                    }
                    else if (topInThis)
                    {   // Komponenta je ve dvou různých bandech, v tomto má svůj horní okraj, a je přilepená jen nahoru (docela běžná situace):
                        // Limit výšky tohoto bandu je dán horním okrajem (Top), dimenzí Begin, tak aby tento band nepřekryl její dolní okraj:
                        minHeight = relRec.YTopBegin;
                    }
                    break;

            }
            return minHeight;
        }
        #endregion
        #endregion
        #region Data pro tento Band
        #endregion
        #region Serializace
        /// <summary>
        /// Zajistí serializaci všech svých hodnot do předaného XML elementu.
        /// Element vytváří nadřízená vrstva (ReportDocument), úkolem komponenty je uložit do XML elementu všechny své hodnoty.
        /// Každá třída ukládá své hodnoty, před tím volá base.XmlSerializeTo(node), aby předkové uložili svoje data.
        /// </summary>
        /// <param name="node"></param>
        public virtual void XmlSerializeTo(XElement node)
        {
            node.SetAttributeValue("Type", this.GetType().Name);
            node.SetAttributeValue("BandId", Convertor.Int32ToString(this.BandId));
            node.SetAttributeValue("OwnerBandId", Convertor.Int32ToString(this.OwnerBandId));
            node.SetAttributeValue("HeaderText", this.HeaderText);
            if (this.ColumnCount > 1)
                node.SetAttributeValue("ColumnsCount", Convertor.Int32ToString(this.ColumnCount));
            node.SetAttributeValue("Height", Convertor.SingleToString(this.LogicalHeight));
            node.SetAttributeValue("HeaderBackColor", Convertor.ColorToString(this.HeaderBackColor));
            if (this.IsCollapsed)
                node.SetAttributeValue("IsCollapsed", Convertor.BooleanToString(this.IsCollapsed));
            this.XmlSerializeDataTo(node);
        }
        /// <summary>
        /// Načte do sebe veškerá data z XML nodu.
        /// Volitelně přepíše i svoje vlastní ID (pokud je overrideId = true). To se používá při čtení z XML dokumentu (Load, Revert, Undo, Redo).
        /// V případě Cut/Copy + Paste se overrideId předává false = vzniká nový téměř identický objekt k objektu původnímu, ale s novým ID.
        /// </summary>
        /// <param name="node">XML element</param>
        /// <param name="overrideId">Požadavek na načtení i vlastního ID (true = načíst / false = ponechat stávající)</param>
        public virtual void XmlFillFrom(XElement node, bool overrideId)
        {
            if (overrideId)
                this.BandId = (Int32)Convertor.StringToInt32(ReportDocument.XmlGetXAttributeValue(node, "BandId"));
            this.OwnerBandId = (Int32)Convertor.StringToInt32(ReportDocument.XmlGetXAttributeValue(node, "OwnerBandId"));
            this.HeaderText = ReportDocument.XmlGetXAttributeValue(node, "HeaderText");
            this.ColumnCount = (Int32)Convertor.StringToInt32(ReportDocument.XmlGetXAttributeValue(node, "ColumnsCount"));
            this.LogicalHeight = (Single)Convertor.StringToSingle(ReportDocument.XmlGetXAttributeValue(node, "Height"));
            this.HeaderBackColor = (Color)Convertor.StringToColor(ReportDocument.XmlGetXAttributeValue(node, "HeaderBackColor"));
            this.IsCollapsed = (Boolean)Convertor.StringToBoolean(ReportDocument.XmlGetXAttributeValue(node, "IsCollapsed"));
            this.XmlFillDataFrom(node, overrideId);
        }
        /// <summary>
        /// Statický konstruktor konkrétní instance, která je popsána v daném XML elementu.
        /// Jde o object factory metodu, vrací konkrétního potomka (podle hodnoty atributu Type).
        /// Pokud nelze objekt vytvořit, vrací null.
        /// </summary>
        /// <param name="node">XML element</param>
        /// <param name="overrideId">Požadavek na načtení i vlastního ID (true = načíst / false = ponechat stávající)</param>
        /// <returns></returns>
        public static ReportBand XmlDeserializeFrom(XElement node, bool overrideId)
        {
            if (node == null) return null;

            XAttribute typeAtt = node.Attribute("Type");
            if (typeAtt == null || String.IsNullOrEmpty(typeAtt.Value)) return null;

            Type baseType = typeof(ReportBand);
            string typeName = baseType.Namespace + "." + typeAtt.Value;
            Type targetType = (baseType.Assembly.GetTypes().FirstOrDefault(t => t.Namespace + "." + t.Name == typeName));
            if (targetType == null) return null;

            ReportBand target = (System.Activator.CreateInstance(targetType) as ReportBand);
            if (target == null) return null;

            target.XmlFillFrom(node, overrideId);

            return target;
        }
        #endregion
		#region IRevertItem Members
		IComparable IRevertItem.ObjectId
		{
			get { return new ItemId(ItemType.Band, this.BandId); }
		}
		string IRevertItem.GetData()
		{
			XElement element = new XElement(ReportDocument.XML_BAND);
			this.XmlSerializeTo(element);
			return element.ToString();
		}
		void IRevertItem.SetData(string data)
		{
			XElement element = XElement.Parse(data);
			this.XmlFillFrom(element, true);
		}
		#endregion
    }
	#region class ReportBandLinearData : Třída zahrnující průběžná data pro podporu linearizace ReportBands
	/// <summary>
	/// ReportBandLinearData : Třída zahrnující průběžná data pro podporu linearizace ReportBands v rekurzivní metodě ReportBand.GetEditorBandList()
    /// </summary>
    internal class ReportBandLinearData : IDisposable
    {
        /// <summary>
        /// Konstruktor, volaný na úrovni ReportDocument
        /// </summary>
        /// <param name="args">Data eventu, obsahují referenci na ViewPort</param>
        /// <param name="visualTotalWidth">Celá šířka prostoru band, vizuální, v pixelech</param>
        /// <param name="visualCollapsedHeight">Výška band, který je Collapsed, vizuální, v pixelech</param>
        /// <param name="innerBands">Seznam vnitřních Bands</param>
        /// <param name="innerComponents">Seznam vnitřních Components</param>
        internal ReportBandLinearData(EditEventArgs args, float visualTotalWidth, float visualCollapsedHeight, Dictionary<int, List<ReportBand>> innerBands, Dictionary<int, List<ReportComponent>> innerComponents)
        {
            this.VisualTotalWidth = visualTotalWidth;
            this.VisualCollapsedHeight = visualCollapsedHeight;
            this.LogicalCollapsedHeight = args.ViewPortConvertor.LogicalFromVisual(visualCollapsedHeight);
            this.InnerBands = innerBands;
            this.InnerComponent = innerComponents;
            this.LogicalY = 0f;
			this.VisualX = 0f;
            this.VisualY = 0f;
            this.ResultBandList = new List<ReportBand>();
            this.ResultComponentList = new List<ReportComponent>();
            this.ResultDocumentSize = SizeF.Empty;
        }
		/// <summary>
		/// Celková šířka záhlaví v pixelech
		/// </summary>
        internal float VisualTotalWidth { get; private set; }
		/// <summary>
		/// Výška prvku pokud je IsCollapsed, vizuální pixely
		/// </summary>
        internal float VisualCollapsedHeight { get; private set; }
        /// <summary>
        /// Výška prvku pokud je IsCollapsed, logické jednotky
        /// </summary>
        internal float LogicalCollapsedHeight { get; private set; }
        /// <summary>
        /// Souřadnice Y, logická, průběžná
        /// </summary>
        internal float LogicalY { get; set; }
        /// <summary>
		/// Souřadnice X, vizuální, průběžná
		/// </summary>
		internal float VisualX { get; set; }
		/// <summary>
		/// Souřadnice Y, vizuální, průběžná
		/// </summary>
        internal float VisualY { get; set; }
		/// <summary>
		/// Vizuální bod počátku, daný souřadnicemi this.VisualX, this.VisualY
		/// </summary>
		internal PointF VisualPoint { get { return new PointF(this.VisualX, this.VisualY); } }
        /// <summary>
        /// Soupis InnerBands
        /// </summary>
        protected Dictionary<int, List<ReportBand>> InnerBands { get; private set; }
        /// <summary>
        /// Soupis InnerComponents
        /// </summary>
        protected Dictionary<int, List<ReportComponent>> InnerComponent { get; private set; }
        /// <summary>
        /// Vrací aktuálně platný seznam InnerBands = prvky ReportBand, které jako svůj OwnerBandId uvádějí hodnotu bandId, předávanou do této funkce.
        /// </summary>
        /// <param name="bandId"></param>
        /// <returns></returns>
        internal List<ReportBand> GetInnerBands(int bandId)
        {
            List<ReportBand> list;
            if (this.InnerBands.TryGetValue(bandId, out list))
                return new List<ReportBand>(list);
            return new List<ReportBand>();
        }
        /// <summary>
        /// Vrací aktuálně platný seznam InnerComponents = prvky ReportComponent, které jako svůj OwnerBandId uvádějí hodnotu bandId, předávanou do této funkce.
        /// </summary>
        /// <param name="bandId"></param>
        /// <returns></returns>
        internal List<ReportComponent> GetInnerComponents(int bandId)
        {
            List<ReportComponent> list;
            if (this.InnerComponent.TryGetValue(bandId, out list))
                return new List<ReportComponent>(list);
            return new List<ReportComponent>();
        }
        /// <summary>
		/// Výsledný seznam prvků, průběžně se do něj přidává
		/// </summary>
        internal List<ReportBand> ResultBandList { get; private set; }
        /// <summary>
        /// Výsledný seznam komponent, průběžně se do něj přidává
        /// </summary>
        internal List<ReportComponent> ResultComponentList { get; private set; }
        /// <summary>
        /// Rozměr dokumentu v editoru (nikoli při tisku), v logických jednotkách = milimetry.
        /// Rozměr je určen vždy po dokončení metody this.PrepareDraw() = příprava k zobrazení
        /// </summary>
        internal SizeF ResultDocumentSize { get; private set; }
        /// <summary>
        /// Nastaví hodnotu this.ResultDocumentSize.Width na hodnotu danou parametrem width, 
        /// pokud tato je větší než stávající this.ResultDocumentSize.Width.
        /// </summary>
        /// <param name="width"></param>
        internal void SetResultDocumentWidth(float width)
        {
            if (width > this.ResultDocumentSize.Width)
                this.ResultDocumentSize = new SizeF(width, this.ResultDocumentSize.Height);
        }
        /// <summary>
        /// Nastaví hodnotu this.ResultDocumentSize.Height na hodnotu danou parametrem height, 
        /// pokud tato je větší než stávající this.ResultDocumentSize.Height.
        /// </summary>
        /// <param name="height"></param>
        internal void SetResultDocumentHeight(float height)
        {
            if (height > this.ResultDocumentSize.Height)
                this.ResultDocumentSize = new SizeF(this.ResultDocumentSize.Width, height);
        }
        /// <summary>
        /// IDisposable
        /// </summary>
        void IDisposable.Dispose()
        {
            this.InnerBands = null;
            this.InnerComponent = null;
            this.ResultBandList = null;
            this.ResultComponentList = null;
        }
    }
	#endregion
    /// <summary>
    /// Druh bandu
    /// </summary>
    public enum ReportBandType
    {
        None = 0,
        /// <summary>
        /// Root Band
        /// </summary>
        Root,
        /// <summary>
        /// Titulek, tiskne se jedenkrát na úvod reportu
        /// Může si vyžádat potlačení tisku záhlaví i zápatí stránky,
        /// tisk na samostatnou stránku, číslování stránek až po stránce následující po tomto titulku..
        /// </summary>
        Title,
        /// <summary>
        /// Záhlaví stránky, tiskne se vždy když začíná stránka
        /// </summary>
        PageHeader,
        /// <summary>
        /// Záhlaví sloupce
        /// </summary>
        ColumnHeader,
        /// <summary>
        /// Záhlaví skupiny, tiskne se jedenkrát na začátku skupiny 
        /// a pak případně na každé nové stránce po PageHeader, pokud je v Bandu uvedeno
        /// </summary>
        GroupHeader,
        /// <summary>
        /// Grupa, tento Band typicky nemá položky ale definuje pravidla pro grupování.
        /// </summary>
        Group,
        /// <summary>
        /// Detail, odpovídá jednomu řádku v tabulce s daty
        /// </summary>
        Detail,
        /// <summary>
        /// Patka skupiny, tiskne se typicky jedenkrát na konci skupiny.
        /// </summary>
        GroupFooter,
        /// <summary>
        /// Patka stránky, tiskne se vždy když začíná stránka
        /// </summary>
        PageFooter,
        /// <summary>
        /// Sumář sestavy, tiskne se jedenkrát na konci reportu.
        /// Může si vyžádat potlačení tisku záhlaví i zápatí stránky, tisk na samostatnou stránku atd.
        /// </summary>
        Summary
    }
}
